/**
 * de.herberlin.sudoku.Main
 */
package de.herberlin.sudoku;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;

import de.herberlin.sudoku.gui.DesignFactory;
import de.herberlin.sudoku.gui.HelpWindow;
import de.herberlin.sudoku.gui.LevelOption;
import de.herberlin.sudoku.gui.SimpleSwingPlayground;
import de.herberlin.sudoku.gui.design.Box;
import de.herberlin.sudoku.gui.design.House;
import de.herberlin.sudoku.gui.design.SimpleSwing;
import de.herberlin.sudoku.gui.design.Women;

/**
 * Starts the application
 *
 * @author herberlin
 * @created 14.06.2006
 */
public class Main extends JFrame implements ActionListener {

	private static Logger logger = Logger.getLogger(Main.class.getName());

	private Sudoku sudoku = null;

	private SimpleSwingPlayground playground = null;

	private Map menuItemMap = null;

	private LevelOption lastLevel = LevelOption.MEDIUM;

	public static final String MENU_SEP = "MenuSep";

	private static void setLogLevel() {

		Handler handler = new ConsoleHandler();

		handler.setLevel(Level.ALL);

		Logger.getLogger("de.herberlin.sudoku.Sudoku").setLevel(Level.INFO);
		Logger.getLogger("de.herberlin.sudoku.Main").setLevel(Level.ALL);
		Logger.getLogger("de.herberlin.sudoku.SudokuFactory").setLevel(
				Level.INFO);

		Logger logger = Logger.getLogger("de.herberlin");
		logger.setUseParentHandlers(false);
		logger.addHandler(handler);
		logger.setLevel(Level.INFO);

		try {
			Handler file = new FileHandler("./sudoku_log.txt", false);
			file.setLevel(Level.INFO);
			file.setFormatter(new SimpleFormatter());
			logger.addHandler(file);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public Main() {
		super("Sudoku");
		setTitle("Sudoku");
		logger.info("Starting sudoku..");
		try {
			// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			UIManager.setLookAndFeel(UIManager
					.getCrossPlatformLookAndFeelClassName());
		} catch (Exception e) {
			logger.warning(e + "");
		}

		if (Locale.GERMAN.getLanguage().equals(Locale.getDefault().getLanguage())) {
			Main.locale=Locale.GERMAN;
		}

		this.setJMenuBar(initMenuBar());

		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				actionExit();
			}
		});

		JLabel label = new JLabel(getMessage("message.new"));
		this.getContentPane().add(label, BorderLayout.NORTH);

		JButton button = new JButton(getMessage("menu.file.new").replaceFirst("_",""));
		button.addActionListener(this);
		button.setActionCommand("menu.file.new");

		JButton button2 = new JButton(getMessage("menu.file.open").replaceFirst("_",""));
		button2.addActionListener(this);
		button2.setActionCommand("menu.file.open");

		JPanel p = new JPanel();
		p.add(button);
		p.add(button2);
		getContentPane().add(p, BorderLayout.CENTER);

		this.setVisible(true);
		pack();

		// Start the Factory
		SudokuFactory.getInstance();
	}

	private void createProgressbar() {

		getContentPane().removeAll();

		JProgressBar progBar = new JProgressBar();
		progBar.setIndeterminate(true);
		progBar.setString("   " + Main.getMessage("message.wait") + "   ");
		progBar.setStringPainted(true);

		JPanel p = new JPanel(new BorderLayout());
		p.add(progBar, BorderLayout.CENTER);

		getContentPane().add(p, BorderLayout.CENTER);
		validate();
		pack();
	}

	public static void showError(Frame parent, Exception e) {

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		logger.log(Level.ALL, e + "", e);
		try {
			e.printStackTrace(new PrintStream(out));
			out.close();
		} catch (IOException e1) {
			logger.warning(e1 + "");
		}

		final JDialog dialog=new JDialog(parent);
		Container c=dialog.getContentPane();
		c.setLayout(new BorderLayout());
		c.add(new JLabel(getMessage("message.error")),BorderLayout.NORTH);
		c.add(new JScrollPane(new JTextArea(out.toString())),BorderLayout.CENTER);
		JButton close=new JButton("OK");
		close.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				dialog.dispose();
			}

		});
		JPanel p=new JPanel(new FlowLayout(FlowLayout.RIGHT));
		p.add(close);
		c.add(p,BorderLayout.SOUTH);
		dialog.setSize(320,240);
		dialog.setVisible(true);
//		JOptionPane.showMessageDialog(parent, text + out.toString(), e
//				.getMessage(), JOptionPane.ERROR_MESSAGE);

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			setLogLevel();
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Error initializin log system.", e);
		}
		new Main();
	}

	private JMenu createMenu(Class btnClass, String section, String[] keys) {
		JMenu menu = new JMenu();
		initMenuItem(menu, section);
		ButtonGroup buttonGroup = new ButtonGroup();

		for (int i = 0; i < keys.length; i++) {

			if (JRadioButtonMenuItem.class.equals(btnClass)) {

				if (MENU_SEP.equals(keys[i])) {
					menu.addSeparator();
					buttonGroup = new ButtonGroup();
				} else {
					JRadioButtonMenuItem item = new JRadioButtonMenuItem();
					initMenuItem(item, section + "." + keys[i]);
					menu.add(item);
					buttonGroup.add(item);
				}

			} else {
				// Create normal menu item
				if (MENU_SEP.equals(keys[i])) {
					menu.addSeparator();
				} else {
					JMenuItem item = new JMenuItem();
					initMenuItem(item, section + "." + keys[i]);
					menu.add(item);
				}
			}
		}
		return menu;
	}

	private JMenuBar initMenuBar() {

		menuItemMap = new HashMap();
		JMenuBar bar = new JMenuBar();
		bar.add(createMenu(JMenuItem.class, "menu.file", new String[] { "new",
				"resolve", MENU_SEP, "save", "open", MENU_SEP, "print",
				MENU_SEP, "exit" }));
		bar.add(createMenu(JRadioButtonMenuItem.class, "menu.view",
				new String[] { "simple", "house", "box", "women", MENU_SEP,
						"english", "german" }));
		bar.add(createMenu(JMenuItem.class, "menu.help", new String[] { "help",
				"about" }));

		// set some language menu item
		String language = locale.getLanguage();
		String key = "menu.view.english";
		if ("de".equals(language)) {
			key = "menu.view.german";
		}
		JMenuItem menuItem = (JMenuItem) menuItemMap.get(key);
		if (menuItem != null) {
			menuItem.setSelected(true);
		}

		// set design
		key = "menu.view.simple";
		if (DesignFactory.getInstance() instanceof House) {
			key = "menu.view.house";
		}
		menuItem = (JMenuItem) menuItemMap.get(key);
		if (menuItem != null) {
			menuItem.setSelected(true);
		}

		return bar;
	}

	public static String getMessage(String key) {
		ResourceBundle bundle = ResourceBundle
				.getBundle("de.herberlin.sudoku.i18n.message",locale);
		return bundle.getString(key);
	}

	private void initMenuItem(JMenuItem item, String key) {
		String title = getMessage(key);
		logger.fine("title=" + title);
		int pos = title.indexOf("_");
		if (pos >= 0) {
			title = title.replaceAll("_", "");
			item.setMnemonic(title.charAt(pos));
		}
		item.setText(title);
		item.setActionCommand(key);
		item.addActionListener(this);

		menuItemMap.put(key, item);

	}

	public void actionPerformed(ActionEvent e) {

		String action = e.getActionCommand();
		logger.fine("Action=" + action);

		if ("menu.file.new".equals(action)) {
			actionNew();
		} else if ("menu.file.resolve".equals(action)) {
			actionResolve();
		} else if ("menu.file.save".equals(action)) {
			actionSave();
		} else if ("menu.file.open".equals(action)) {
			actionOpen();
		} else if ("menu.file.print".equals(action)) {
			actionPrint();
		} else if ("menu.file.exit".equals(action)) {
			actionExit();
		} else if ("menu.view.simple".equals(action)) {
			actionDesign(SimpleSwing.class);
		} else if ("menu.view.house".equals(action)) {
			actionDesign(House.class);
		} else if ("menu.view.box".equals(action)) {
			actionDesign(Box.class);
		} else if ("menu.view.women".equals(action)) {
			actionDesign(Women.class);
		} else if ("menu.view.english".equals(action)) {
			actionLocale(Locale.ENGLISH);
		} else if ("menu.view.german".equals(action)) {
			actionLocale(Locale.GERMAN);
		} else if ("menu.help.help".equals(action)) {
			try {
				FileService.getInstance().navigate(
						new URL(getMessage("help.url")));
			} catch (Exception e1) {
				showError(this, e1);
			}
		} else if ("menu.help.about".equals(action)) {
			try {
				new HelpWindow("About", getClass().getResource(
						"/help/about.html"));
			} catch (IOException e1) {
				showError(this, e1);
			}
		}

	}

	public static Locale locale=Locale.ENGLISH;

	private void actionLocale(Locale locale) {
		Main.locale=locale;
		JMenuBar bar = initMenuBar();
		this.setJMenuBar(bar);
		doLayout();
	}

	private void actionDesign(Class implClass) {
		DesignFactory.setImplClass(implClass);
		playground.init();
		this.validate();
		this.pack();
		pack();

	}

	private void actionNew() {

		createProgressbar();
		LevelOption levelChoice = (LevelOption) chooseOption(
				"message.choose.level", LevelOption.ALL, lastLevel);

		logger.fine("LevelChoice=" + levelChoice);
		if (levelChoice == null) {
			return;
		}
		lastLevel=levelChoice;

		Container c = this.getContentPane();
		c.setLayout(new BorderLayout());

		Sudoku sodoku = null;
		try {
			sudoku = SudokuFactory.getInstance().getSudoku();
			SudokuFactory.getInstance().setSeverity(sudoku,
					levelChoice.getValue());
		} catch (InterruptedException e) {
			showError(this, e);
			return;
		}
		installSudoku(sudoku);
	}

	private void installSudoku(Sudoku sudoku) {
		this.sudoku = sudoku;
		getContentPane().removeAll();
		playground = new SimpleSwingPlayground(sudoku);
		getContentPane().add(playground, BorderLayout.CENTER);
		this.validate();
		pack();

	}

	private void actionResolve() {
		if (confirm(this,"message.confirm.resolve")) {
			Field[][] fields = sudoku.getFields();
			for (int m = 0; m < fields.length; m++) {
				for (int n = 0; n < fields[m].length; n++) {
					fields[m][n].setResolved();
				}
			}
		}
	}

	private void actionSave() {

		try {
			OutputStream stream = FileService.getInstance().getOutputStream(
					this);
			if (stream == null)
				return;
			ObjectOutputStream out = new ObjectOutputStream(stream);
			out.writeObject(sudoku);
			out.close();
		} catch (Exception e) {
			showError(this, e);
			return;
		}
	}

	private void actionOpen() {

		try {
			InputStream stream = FileService.getInstance().getInputStream(this);
			if (stream == null)
				return;
			ObjectInputStream in = new ObjectInputStream(stream);
			Sudoku sudoku = (Sudoku) in.readObject();
			in.close();
			installSudoku(sudoku);
		} catch (Exception e) {
			showError(this, e);
		}
	}


	private void actionPrint() {

		if (playground!=null) {
			setExtendedState(JFrame.NORMAL);
			pack();
			try {
				FileService.getInstance().print(playground);
			} catch (Exception e) {
				showError(this,e);
			}
		}
	}
	private void actionExit() {

		int result = JOptionPane.NO_OPTION;

		if (sudoku != null) {
			result = JOptionPane.showConfirmDialog(this,
					getMessage("message.confirm.save"),
					getMessage("message.confirm.title"),
					JOptionPane.YES_NO_CANCEL_OPTION);
		}
		if (result == JOptionPane.YES_OPTION) {
			actionSave();
			System.exit(0);
		} else if (result == JOptionPane.NO_OPTION) {
			System.exit(0);
		} else {
			// do nothing on cancel;
		}
	}

	public static boolean confirm(Component parent,String messageKey) {

		int result = JOptionPane.showConfirmDialog(parent,
				getMessage(messageKey), getMessage("message.confirm.title"),
				JOptionPane.OK_CANCEL_OPTION);
		return result == JOptionPane.OK_OPTION;
	}

	private Object chooseOption(String messageKey, Object[] options,
			Object defaultOption) {

			JOptionPane.setDefaultLocale(locale);
		return JOptionPane.showInputDialog(this, getMessage(messageKey),
				getMessage("message.confirm.title"),
				JOptionPane.QUESTION_MESSAGE, null, options, defaultOption);
	}
}
